package consensus

import (
	"accounts"
	"bufio"
	"bytes"
	"client"
	"core"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/ethereum/go-ethereum/rlp"
	"io"
	"math/big"
	"merkletree"
	"os"
	"protocol"
	"time"
)

type Consensus struct {
	protocol.Service
}

var (
	Cons                        *Consensus
	transactionPool             = make(chan core.Transaction, 10)
	ContendBlockChainConfig     = "ContendBlockChain.data"
	TransactionBlockChainConfig = "TransactionBlockChain.data"
	Difficult                   = 20
	other                       = make(chan struct{}, 1) //其他人是否竞争胜出
	changepbft                  = make(chan struct{}, 1)
	continuepow                 = make(chan struct{}, 1)
	serverisme                  = false //是否为主节点
	running                     = false
	serverip                    string
)

func NewConsensus() {
	Cons = &Consensus{
		protocol.Service{
			Name:        "Consensus",
			SendPool:    nil,
			ReceivePool: make(chan protocol.Message, 1024),
		},
	}
}

//新建创世竞争区块 如果检测到其他节点出块成功 停止
func (c *Consensus) NewGenesisContendBlock(stop chan struct{}) *core.ContendBlock {
	block := core.ContendBlock{
		Index:                0,
		Timestamp:            uint64(time.Now().Unix()),
		Difficult:            uint64(Difficult),
		Nonce:                0,
		PublicKey:            client.ClientInfo.PublicKey,
		TransactionBlockHash: []byte(""),
		PrevContendBlockHash: []byte(""),
		Hash:                 []byte(""),
		Signature:            []byte(""),
	}

	target := big.NewInt(1)
	target.Lsh(target, uint(256-block.Difficult)) //左移256个 target bits位

	for ; ; block.Nonce++ {

		select {
		case <-stop:
			return nil
		default:
		}
		temp, err := rlp.EncodeToBytes(block)
		if err != nil {
			return nil
		}
		hashTemp, _ := core.CalculateHash(temp, core.SM3)
		var b big.Int
		b.SetBytes(hashTemp)
		if b.Cmp(target) <= 0 {
			block.Hash = hashTemp
			block.Signature, err = client.ClientInfo.PrivateKey.Sign(rand.Reader, block.Hash, nil) // 签名
			if err != nil {
				fmt.Println(err)
				return nil
			}
			return &block
		}
	}
}

//新建创世交易区块
func (c *Consensus) NewGenesisTransactionBlock() *core.TransactionBlock {
	block := core.TransactionBlock{
		core.TransactionBlockHeader{
			Index:                    0,
			Timestamp:                uint64(time.Now().Unix()),
			ContendBlockHash:         core.ContendBlockChain[uint64(len(core.ContendBlockChain)-1)].Hash,
			PublicKey:                client.ClientInfo.PublicKey,
			PrevTransactionBlockHash: []byte(""),
			MerkleRootHash:           []byte(""),
			Hash:                     []byte(""),
			Signature:                []byte(""),
		},
		core.TransactionBlockBody{
			Trans: nil,
		},
	}

	for {
		if uint64(time.Now().Unix())-block.Timestamp >= 2 { //2秒必须出块
			break
		}
		select {
		case tran := <-transactionPool:
			block.Trans = append(block.Trans, tran)
		default:
			continue
		}
	}

	if block.Trans == nil { //如果交易池中没有数据，本区块没有交易
		block.MerkleRootHash = []byte("")
	} else {
		var list []merkletree.Content
		//转换切片的类型  好蠢呀
		for i := range block.Trans {
			list = append(list, block.Trans[i])
		}

		t, err := merkletree.NewTree(list)
		if err != nil {
			fmt.Println(err)
		}

		block.MerkleRootHash = t.MerkleRoot()
	}
	temp, err := rlp.EncodeToBytes(block)
	if err != nil {
		return nil
	}
	block.Hash, _ = core.CalculateHash(temp, core.SM3)
	block.Signature, err = client.ClientInfo.PrivateKey.Sign(rand.Reader, block.Hash, nil) // 签名
	if err != nil {
		return nil
	}
	return &block
}

func (c *Consensus) StartPoW(next chan struct{}) {
	for {
		block := &core.ContendBlock{
			Index:                core.ContendBlockChain[uint64(len(core.ContendBlockChain)-1)].Index + 1,
			Timestamp:            uint64(time.Now().Unix()),
			Difficult:            uint64(Difficult),
			Nonce:                0,
			PublicKey:            client.ClientInfo.PublicKey,
			TransactionBlockHash: []byte(""),
			PrevContendBlockHash: core.ContendBlockChain[uint64(len(core.ContendBlockChain)-1)].Hash,
			Hash:                 []byte(""),
			Signature:            []byte(""),
		}
		if len(core.ContendBlockChain) == 1 { //第二个竞争区块是没有交易区块引用的
			block.TransactionBlockHash = []byte("")
		} else {
			for i := len(core.TransactionBlockChain) - 1; i >= 0; i-- { //从后向前遍历
				if core.TransactionBlockChain[uint64(i)].Timestamp <= core.ContendBlockChain[uint64(len(core.ContendBlockChain)-1)].Timestamp {
					block.TransactionBlockHash = core.TransactionBlockChain[uint64(i)].Hash
				}
			}
		}

		target := big.NewInt(1)
		target.Lsh(target, uint(256-block.Difficult)) //左移256个 target bits位

	Exit:
		for ; ; block.Nonce++ {

			select {
			case <-next:
				break Exit
			default:
			}
			temp, err := rlp.EncodeToBytes(block)
			if err != nil {
				fmt.Println(err)
				return
			}
			hashTemp, _ := core.CalculateHash(temp, core.SM3)
			var b big.Int
			b.SetBytes(hashTemp)
			if b.Cmp(target) <= 0 {
				block.Hash = hashTemp
				block.Signature, err = client.ClientInfo.PrivateKey.Sign(rand.Reader, block.Hash, nil) // 签名
				if err != nil {
					fmt.Println(err)
					return
				}
				fmt.Println("竞争区块出块 No", block.Index)
				//fmt.Println(block)
				//成功出块，构造消息
				core.ContendBlockChain[block.Index] = block
				msg := protocol.NewMessage(protocol.NewContendBlock, block)
				c.SendPool <- *msg
				serverisme = true
				changepbft <- struct{}{}
				break Exit
			}
		}
	}
}

func (c *Consensus) ReceiveMsg() {
	for {
		msg := <-c.ReceivePool
		switch msg.Code {
		case protocol.NewContendBlock:
			//消息转换为区块信息
			cb := &core.ContendBlock{}
			err := rlp.DecodeBytes(msg.Payload, cb)
			if err != nil {
				break
			}
			if uint64(len(core.ContendBlockChain)-1) == 0xFFFFFFFFFFFFFFFF {
				fmt.Println("没有区块 无法验证")
				c.CheckPeerContendBlockChain()
				break
			}
			//验证区块的hash是否连续
			if bytes.Equal(cb.PrevContendBlockHash, core.ContendBlockChain[uint64(len(core.ContendBlockChain)-1)].Hash) == false {
				c.CheckPeerContendBlockChain()
				break
			}
			//验证区块的签名
			pub, err := core.UnmarshalPubkey(cb.PublicKey)
			if err != nil {
				fmt.Println(err)
				break
			}
			if pub.Verify(cb.Hash, cb.Signature) == false {
				fmt.Println(err)
				break
			}

			//执行到这里 区块验证完成

			core.ContendBlockChain[cb.Index] = cb
			other <- struct{}{} //通知主线程其他节点已经竞争胜出

		case protocol.NewTransactionBlock:
			//消息转换为区块信息
			fmt.Println("接收到NewTransactionBlock")
			cb := &core.TransactionBlock{}
			err := rlp.DecodeBytes(msg.Payload, cb)
			if err != nil {
				fmt.Println(err)
				break
			}
			fmt.Println(cb)
			if uint64(len(core.TransactionBlockChain)-1) == 0xFFFFFFFFFFFFFFFF {
				fmt.Println("没有区块 无法验证")
				c.CheckPeerTransactionBlockChain()
				break
			}
			//验证区块的hash是否连续
			if bytes.Equal(cb.PrevTransactionBlockHash, core.TransactionBlockChain[uint64(len(core.TransactionBlockChain)-1)].Hash) == false {
				c.CheckPeerTransactionBlockChain()
				break
			}
			//验证公钥是否一致
			if bytes.Equal(core.ContendBlockChain[uint64(len(core.ContendBlockChain)-1)].PublicKey, cb.PublicKey) == false {
				break
			}
			//验证签名
			pub, err := core.UnmarshalPubkey(cb.PublicKey)
			if err != nil {
				break
			}
			if pub.Verify(cb.Hash, cb.Signature) == false {
				break
			}
			//执行到这里 区块验证完成
			core.TransactionBlockChain[cb.Index] = cb

		case protocol.Transaction:
			tx := &core.Transaction{}
			err := rlp.DecodeBytes(msg.Payload, tx)
			if err != nil {
				fmt.Println("Tx Err")
				break
			}
			transactionPool <- *tx
			fmt.Println("PBFT收到交易")
			//虽然不应该在这里构造交易成功消息，但只有在这里才能知道信息的来源IP
			if accounts.CheckTransaction(tx) {
				m := protocol.NewMessage(protocol.TransactionSuccess, *tx)
				m.Source = msg.Source
				c.SendPool <- *m
				fmt.Println("交易确认成功")
			} else {
				m := protocol.NewMessage(protocol.TransactionFail, *tx)
				m.Source = msg.Source
				c.SendPool <- *m
				fmt.Println("交易确认失败")
			}

		case protocol.GetContendChainNo:
			no := uint64(len(core.ContendBlockChain) - 1)
			m := protocol.NewMessage(protocol.ContendChainNo, no)
			m.Source = msg.Source
			c.SendPool <- *m

		case protocol.GetTransactionChainNo:
			no := uint64(len(core.TransactionBlockChain) - 1)
			m := protocol.NewMessage(protocol.TransactionChainNo, no)
			m.Source = msg.Source
			c.SendPool <- *m

		case protocol.ContendChainNo:
			var no uint64
			err := rlp.DecodeBytes(msg.Payload, &no)
			if err != nil {
				fmt.Println("protocol.ContendChainNo DecodeBytes err :", err)
			} else {
				if no == 0xFFFFFFFFFFFFFFFF {
					break
				}
				if core.LatestConetendBlockNo == 0xFFFFFFFFFFFFFFFF {
					core.LatestConetendBlockNo = no
				} else if core.LatestConetendBlockNo < no {
					core.LatestConetendBlockNo = no
				}
				if uint64(len(core.ContendBlockChain)-1) == 0xFFFFFFFFFFFFFFFF {
					for i := uint64(0); i <= no; i++ {
						m := protocol.NewMessage(protocol.SyncContendChain, i)
						m.Source = msg.Source
						c.SendPool <- *m
					}
				} else if uint64(len(core.ContendBlockChain)-1) < no {
					for i := core.ContendBlockChain[uint64(len(core.ContendBlockChain)-1)].Index; i <= no; i++ {
						m := protocol.NewMessage(protocol.SyncContendChain, i)
						m.Source = msg.Source
						c.SendPool <- *m
					}
				}
			}

		case protocol.TransactionChainNo:
			var no uint64
			err := rlp.DecodeBytes(msg.Payload, &no)
			if err != nil {
				fmt.Println("protocol.TransactionChainNo DecodeBytes err :", err)
			} else {
				if no == 0xFFFFFFFFFFFFFFFF {
					break
				}
				if core.LatestTransactionBlockNo == 0xFFFFFFFFFFFFFFFF {
					core.LatestTransactionBlockNo = no
				} else if core.LatestTransactionBlockNo < no {
					core.LatestTransactionBlockNo = no
				}
				if uint64(len(core.TransactionBlockChain)-1) == 0xFFFFFFFFFFFFFFFF {
					for i := uint64(0); i <= no; i++ {
						m := protocol.NewMessage(protocol.SyncTransactionChain, i)
						m.Source = msg.Source
						c.SendPool <- *m
					}
				} else if uint64(len(core.TransactionBlockChain)-1) < no {
					for i := core.TransactionBlockChain[uint64(len(core.TransactionBlockChain)-1)].Index; i <= no; i++ {
						m := protocol.NewMessage(protocol.SyncTransactionChain, i)
						m.Source = msg.Source
						c.SendPool <- *m
					}
				}
			}

		case protocol.SyncContendChain:
			var no uint64
			err := rlp.DecodeBytes(msg.Payload, &no)
			if err != nil {
				fmt.Println("protocol.SyncContendChain DecodeBytes err :", err)
			} else {
				m := protocol.NewMessage(protocol.ContendChain, core.ContendBlockChain[no])
				m.Source = msg.Source
				c.SendPool <- *m
			}

		case protocol.SyncTransactionChain:
			var no uint64
			err := rlp.DecodeBytes(msg.Payload, &no)
			if err != nil {
				fmt.Println("protocol.SyncTransactionChain DecodeBytes err :", err)
			} else {
				m := protocol.NewMessage(protocol.TransactionChain, core.TransactionBlockChain[no])
				m.Source = msg.Source
				c.SendPool <- *m
			}

		case protocol.ContendChain:
			conblock := &core.ContendBlock{}
			err := rlp.DecodeBytes(msg.Payload, conblock)
			if err != nil {
				fmt.Println("protocol.SyncContendChain DecodeBytes err :", err)
			} else {
				core.ContendBlockChain[conblock.Index] = conblock
			}

		//case protocol.TransactionChain:
		//	txblock := &core.TransactionBlock{}
		//	err := rlp.DecodeBytes(msg.Payload, txblock)
		//	if err != nil {
		//		fmt.Println("protocol.SyncContendChain DecodeBytes err :", err)
		//	} else {
		//		core.TransactionBlockChain[txblock.Index] = txblock
		//	}

		case protocol.GetServerIP:
			m := protocol.NewMessage(protocol.ServerIP, serverip)
			m.Source = msg.Source
			c.SendPool <- *m

		case protocol.ServerIP:
			if serverisme == false {
				var s string
				err := rlp.DecodeBytes(msg.Payload, &s)
				if err != nil {
					fmt.Println("protocol.SyncTransactionChain DecodeBytes err :", err)
				} else {
					serverip = s
				}
			}

		}

	}
}

func (c *Consensus) StartPBFTServer(stop chan struct{}) {
	if len(core.TransactionBlockChain) == 0 {
		txblock := c.NewGenesisTransactionBlock()
		core.TransactionBlockChain[txblock.Index] = txblock
	}
	for {
		block := &core.TransactionBlock{
			core.TransactionBlockHeader{
				Index:                    core.TransactionBlockChain[uint64(len(core.TransactionBlockChain)-1)].Index + 1,
				Timestamp:                uint64(time.Now().Unix()),
				ContendBlockHash:         core.ContendBlockChain[uint64(len(core.ContendBlockChain)-1)].Hash,
				PublicKey:                client.ClientInfo.PublicKey,
				PrevTransactionBlockHash: core.TransactionBlockChain[uint64(len(core.TransactionBlockChain)-1)].Hash,
				MerkleRootHash:           []byte(""),
				Hash:                     []byte(""),
				Signature:                []byte(""),
			},
			core.TransactionBlockBody{
				Trans: nil,
			},
		}

		for {
			if uint64(time.Now().Unix())-block.Timestamp >= 2 { //2秒必须出块
				break
			}
			select {
			case tran := <-transactionPool:
				block.Trans = append(block.Trans, tran)
				fmt.Println("添加交易")
			default:
				continue
			}
		}

		if block.Trans == nil { //如果交易池中没有数据，本区块没有交易
			block.MerkleRootHash = []byte("")
		} else {
			var list []merkletree.Content
			//转换切片的类型  好蠢呀
			for i := range block.Trans {
				list = append(list, block.Trans[i])
			}
			t, err := merkletree.NewTree(list)
			if err != nil {
				fmt.Println(err)
			}
			block.MerkleRootHash = t.MerkleRoot()

		}
		temp, err := rlp.EncodeToBytes(block)
		if err != nil {
			fmt.Println(err)
			return
		}
		block.Hash, _ = core.CalculateHash(temp, core.SM3)
		block.Signature, err = client.ClientInfo.PrivateKey.Sign(rand.Reader, block.Hash, nil) // 签名
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println("交易区块出块No ", block.Index)
		//fmt.Println(block)
		//成功出块，构造消息
		core.TransactionBlockChain[block.Index] = block

		msg := protocol.NewMessage(protocol.NewTransactionBlock, *block)
		c.SendPool <- *msg
		select {
		case <-stop:
			running = false
			fmt.Println("节点性质切换")
			return
		default:
		}
	}

}

func (c *Consensus) StartPBFTClient(stop chan struct{}) {

	for {
		select {
		case <-stop:
			running = false
			return
		default:
		}
		time.Sleep(1 * time.Second)
	}

}

func (c *Consensus) GetServerIP() {
	msg := protocol.NewMessage(protocol.GetServerIP, nil)
	c.SendPool <- *msg
}

func (c *Consensus) StartConsensus() {

	//本地加载
	err := LoadContendBlockChain()
	if err != nil {
		fmt.Println(err)
	}
	err = LoadTransactionBlockChain()
	if err != nil {
		fmt.Println(err)
	}

	core.LatestConetendBlockNo = uint64(len(core.ContendBlockChain) - 1)
	core.LatestTransactionBlockNo = uint64(len(core.TransactionBlockChain) - 1)

	go c.ReceiveMsg()

	c.CheckPeerContendBlockChain()
	c.CheckPeerTransactionBlockChain()

	go func() {
		for {
			//fmt.Println("写入竞争区块")
			err := c.SaveContendBlockChain()
			if err != nil {
				fmt.Println(err)
			}
			//fmt.Println("写入交易区块")
			err = c.SaveTransactionBlockChain()
			if err != nil {
				fmt.Println(err)
			}
			time.Sleep(2 * time.Second)
		}
	}()

	//等待同步结束
	for {
		if core.LatestConetendBlockNo == uint64(len(core.ContendBlockChain)-1) && core.LatestTransactionBlockNo == uint64(len(core.TransactionBlockChain)-1) {
			break
		} else {
			time.Sleep(2 * time.Second)
		}
	}

	//开启协程 控制主从节点状态切换
	go func() {
		for {
			select {
			case <-other:
				changepbft <- struct{}{}
				continuepow <- struct{}{}
				serverisme = false
			default:
			}
			time.Sleep(2 * time.Second)
		}
	}()

	if len(core.ContendBlockChain) == 0 {
		fmt.Println("开始创建创世竞争区块")
		conblock := c.NewGenesisContendBlock(changepbft)
		if conblock != nil {
			fmt.Println("创世竞争区块创建完毕")
			serverisme = true
			core.ContendBlockChain[conblock.Index] = conblock
			fmt.Println("竞争区块出块 No", conblock.Index)
			//fmt.Println(conblock)
			fmt.Println("开始创建创世交易区块")
			txblock := c.NewGenesisTransactionBlock()
			core.TransactionBlockChain[txblock.Index] = txblock
			fmt.Println("创世交易区块创建完毕")
			fmt.Println("交易区块出块 No", txblock.Index)
			//fmt.Println(txblock)
		} else {
			serverisme = false
		}
	}

	go func() {
		for {
			fmt.Println("判断节点性质", serverisme)

			if serverisme {
				if running == false {
					fmt.Println("开启PBFT服务器")
					go c.StartPBFTServer(changepbft)
					running = true
				}
			} else {
				if running == false {
					go c.StartPBFTClient(changepbft)
					running = true
				}
			}
			time.Sleep(2 * time.Second)
		}
	}()

	go c.StartPoW(continuepow)

}

func CheckChain() error {

	if len(core.TransactionBlockChain) < len(core.ContendBlockChain) {
		return errors.New("Missing data")
	}

	//检查Transaction链Hash
	for i := uint64(len(core.TransactionBlockChain) - 1); i > 0; i-- {
		if bytes.Equal(core.TransactionBlockChain[i].PrevTransactionBlockHash, core.TransactionBlockChain[i-1].Hash) {
			return errors.New("TransChain Hash Error")
		}
	}

	//检查Contend链Hash
	for i := uint64(len(core.ContendBlockChain) - 1); i > 0; i-- {
		if bytes.Equal(core.ContendBlockChain[i].PrevContendBlockHash, core.ContendBlockChain[i-1].Hash) {
			return errors.New("TransChain Hash Error")
		}
	}

	//交叉检查
	for i := uint64(0); i < uint64(len(core.TransactionBlockChain)); i++ {
		for j := range core.ContendBlockChain {
			if bytes.Equal(core.TransactionBlockChain[i].ContendBlockHash, core.ContendBlockChain[j].Hash) {
				v, ok := core.ContendBlockChain[core.ContendBlockChain[j].Index+1]
				if ok == false {
					break
				} else {
					if core.TransactionBlockChain[i].Timestamp > v.Timestamp {
						return errors.New("交叉验证失败")
					} else {
						break
					}
				}
			}
		}
	}
	return nil
}
func LoadContendBlockChain() error {
	file, err := os.OpenFile(ContendBlockChainConfig, os.O_RDONLY, 0666)
	if err != nil {
		return err
	}
	defer file.Close()
	//建立缓冲区，把文件内容放到缓冲区中
	buf := bufio.NewReader(file)
	for {
		//遇到\n结束读取
		b, errR := buf.ReadBytes('\n')
		if errR != nil {
			if errR == io.EOF {
				break
			}
			fmt.Println(errR.Error())
		}
		c := &core.ContendBlock{}
		err = json.Unmarshal(b, c)
		if err != nil {
			return err
		}
		core.ContendBlockChain[c.Index] = c
	}
	return nil
}

func LoadTransactionBlockChain() error {
	file, err := os.OpenFile(TransactionBlockChainConfig, os.O_RDONLY, 0666)
	if err != nil {
		return err
	}
	defer file.Close()
	//建立缓冲区，把文件内容放到缓冲区中
	buf := bufio.NewReader(file)
	for {
		//遇到\n结束读取
		b, errR := buf.ReadBytes('\n')
		if errR != nil {
			if errR == io.EOF {
				break
			}
			fmt.Println(errR.Error())
		}
		t := &core.TransactionBlock{}
		err = json.Unmarshal(b, t)
		if err != nil {
			return err
		}
		core.TransactionBlockChain[t.Index] = t
	}
	return nil
}

func (c *Consensus) SaveContendBlockChain() error {
	file, err := os.OpenFile(ContendBlockChainConfig, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer file.Close()
	for i := uint64(0); i < uint64(len(core.ContendBlockChain)); i++ {
		b, err := json.Marshal(core.ContendBlockChain[i])
		if err != nil {
			return err
		}
		b = append(b, '\n')
		_, errW := file.Write(b)
		if errW != nil {
			fmt.Println(errW.Error())
			return errW
		}
	}

	return nil
}

func (c *Consensus) SaveTransactionBlockChain() error {
	file, err := os.OpenFile(TransactionBlockChainConfig, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer file.Close()
	for i := uint64(0); i < uint64(len(core.TransactionBlockChain)); i++ {
		b, err := json.Marshal(core.TransactionBlockChain[i])
		if err != nil {
			return err
		}
		b = append(b, '\n')
		_, errW := file.Write(b)
		if errW != nil {
			fmt.Println(errW.Error())
			return errW
		}
	}
	return nil
}

func (c *Consensus) CheckPeerContendBlockChain() {
	msg := protocol.NewMessage(protocol.GetContendChainNo, nil)
	fmt.Println(msg)
	c.SendPool <- *msg
}

func (c *Consensus) CheckPeerTransactionBlockChain() {
	msg := protocol.NewMessage(protocol.GetTransactionChainNo, nil)
	fmt.Println(msg)
	c.SendPool <- *msg
}
